#include <kutils.h>
#include <string.h>
#include "syscall.h"
#include "vfs.h"
#include "display.h"
#include "task.h"

u32int get_filedes(u32int address);
u32int get_address(u32int filedes);
void clear_filedes(u32int filedes);
void open_handler(syscall_regs_t *r);
void close_handler(syscall_regs_t *r);
void read_handler(syscall_regs_t *r);
void write_handler(syscall_regs_t *r);
void readdir_handler(syscall_regs_t *r);
void finddir_handler(syscall_regs_t *r);

extern fs_node_t *fs_root;
extern task_t* current_task;
u32int filedes_table[33];// 3..32 are valid, 0..2 are reserved (standard streams).

u32int get_filedes(u32int address)
{
	u32int i = 0;
	for(i = 3; i < 32; i++)
	{
		if(filedes_table[i] == 0)
		{
			filedes_table[i] = address;
			return i;
		}
	}
	return 0;//none avaliable
}

u32int get_address(u32int filedes)
{
	switch(filedes)
	{
		case 0:
			return (u32int)current_task->stdin;
			break;
		case 1:
			return (u32int)current_task->stdout;
			break;
		case 2:
			return (u32int)current_task->stdout;
			break;
		default:
			return filedes_table[filedes];
			break;
	}
}

void clear_filedes(u32int filedes)
{
	filedes_table[filedes] = 0;
}

void open_handler(syscall_regs_t *r)
{
	//write_string(" open(");
	//write_string((s8int*)r->arg1);
	//write_string(") ");
	fs_node_t* file = open_fs((s8int*)r->arg1);
	if(file)
	{
		u32int des = get_filedes((u32int)file);
		//write_string(" filedes: ");
		//write_hex(des);
		r->res1 = des;
		r->success = 1;
	}
	else
	{
		r->success = 0;
	}
}

void close_handler(syscall_regs_t *r)
{
	fs_node_t* node = (fs_node_t*)get_address(r->arg1);
	//write_string(" close(");
	//write_hex(r->arg1);
	//write_string(") ");
	clear_filedes(r->arg1);
	if(close_fs(node))
	{
		r->success = 1;
	}
	else
	{
		r->success = 0;
	}
}

void read_handler(syscall_regs_t *r)
{
	seek_fs((fs_node_t*)get_address(r->arg1), r->arg2); //TODO: add seek syscall to eliminate this
	if(read_fs((fs_node_t*)get_address(r->arg1), r->arg4, (u8int*)r->arg3))
	{
		r->res1 = r->arg4;
		r->success = 1;
	}
	else
	{
		r->res1 = 0;
		r->success = 0;
	}
}

void write_handler(syscall_regs_t *r)
{
	seek_fs((fs_node_t*)get_address(r->arg1), r->arg2); //TODO: add seek syscall to eliminate this
	if(write_fs((fs_node_t*)get_address(r->arg1), r->arg4, (u8int*)r->arg3))
	{
		r->res1 = r->arg4;
		r->success = 1;
	}
	else
	{
		r->res1 = 0;
		r->success = 0;
	}
}

void readdir_handler(syscall_regs_t *r)
{
	fs_node_t* node = finddir_fs(fs_root, (s8int*)r->arg1);
	struct dirent* entry = readdir_fs(node, r->arg2);
	if(entry)
	{
		strcpy((s8int*)r->arg3, (s8int*)entry->name);
		r->res1 = r->arg3;
		r->success = 1;
	}
	else
	{
		r->success = 0;
	}
}

void finddir_handler(syscall_regs_t *r)
{
	fs_node_t* current_node = finddir_fs(fs_root, (s8int*)r->arg1);
	if(current_node)
	{
		fs_node_t* new_node = finddir_fs(current_node, (s8int*)r->arg2);
		if(new_node)
		{
			r->res1 = (u32int)fs_fill_path(new_node, (s8int*)r->arg3);
			r->success = 1;
		}
		else
		{
			r->success = 0;
		}
	}
	else
	{
		r->success = 0;
	}
}

void init_vfs_interface(void)
{
	install_syscall(SYS_OPEN, open_handler);
	install_syscall(SYS_CLOSE, close_handler);
	install_syscall(SYS_READ, read_handler);
	install_syscall(SYS_WRITE, write_handler);
	install_syscall(SYS_READDIR, readdir_handler);	
	install_syscall(SYS_FINDDIR, finddir_handler);	
}
